package com.ggb.reactive.controller.admin

import com.ggb.reactive.constant.*
import com.ggb.reactive.domain.dto.DishDTO
import com.ggb.reactive.domain.dto.DishPageQueryDTO
import com.ggb.reactive.domain.entity.Dish
import com.ggb.reactive.domain.vo.DishVO
import com.ggb.reactive.exception.DeletionNotAllowedException
import com.ggb.reactive.model.PageResult
import com.ggb.reactive.model.Result
import com.ggb.reactive.repository.DishFlavorRepository
import com.ggb.reactive.repository.DishRepository
import com.ggb.reactive.service.DishService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.constraints.NotNull
import lombok.RequiredArgsConstructor
import org.springframework.beans.BeanUtils
import org.springframework.transaction.annotation.Transactional
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import java.time.Instant
import com.ggb.reactive.extension.log
import com.ggb.reactive.repository.SetmealDishRepository
import org.springframework.data.redis.core.ReactiveRedisTemplate
import org.springframework.data.redis.core.ScanOptions
import reactor.core.scheduler.Schedulers

@RestController
@RequestMapping("/admin/dish")
@RequiredArgsConstructor
@Tag(name = "菜品管理")
class DishController(
    val dishRepository: DishRepository,
    val dishFlavorRepository: DishFlavorRepository,
    val setmealDishRepository: SetmealDishRepository,
    val dishService: DishService,
    val reactiveRedisTemplate: ReactiveRedisTemplate<String, Any>
) {

    /**
     * 新增菜品
     */
    @Transactional
    @PostMapping
    @Operation(summary = "新增菜品")
    fun add(@RequestBody dishDTO: DishDTO): Mono<Result<String>> {
        val dish = Dish()
        BeanUtils.copyProperties(dishDTO, dish)
        val key = "dish_${dishDTO.categoryId}"
        return dishRepository.save(dish).flatMap { dishModel ->

            val flavors = dishDTO.flavors?.map {
                it.dishId = dishModel.id!!
                it
            }
            if (!flavors.isNullOrEmpty()) {
                dishFlavorRepository.saveAll(flavors)
                    .then(cleanCache(key)) // 在保存 flavors 后清除缓存
                    .then(Mono.just(Result.success(ACTION_SUCCESS)))
            } else {
                cleanCache(key).then(Mono.just(Result.success(ACTION_SUCCESS)))
            }
        }
            .onErrorResume {
                Mono.just(Result.error(ACTION_FAIL)) // 在出现错误时返回错误消息
            }
    }

    /**
     * 启用禁用菜品
     */
    @Operation(summary = "启用禁用菜品")
    @PostMapping("/status/{status}")
    fun startOrStop(@PathVariable status: Int?, id: Long?, exchange: ServerWebExchange): Mono<Result<String>> {
        if (id == null) return Mono.just(Result.error("id名不能为空"))
        if (status == null) return Mono.just(Result.error("status不能为空"))
        val userId = (exchange.attributes[USER_ID] as Int).toLong()
        return dishRepository.updateStatusById(id, status, Instant.now(), userId)
            .flatMap {
                cleanCache("dish_*").then(Mono.just(Result.success<String>()))
            }
            .onErrorResume {
                Mono.just(Result.error(ACTION_FAIL))
            }
    }

    /**
     * 菜品分页查询
     */
    @Operation(summary = "菜品分页查询")
    @GetMapping("/page")
    fun page(dishPageQueryDTO: DishPageQueryDTO): Mono<Result<PageResult<Dish>>> =
        dishService.findDishesByCriteria(dishPageQueryDTO)


    /**
     * 菜品批量删除
     */
    @Operation(summary = "菜品批量删除")
    @DeleteMapping
    @Transactional
    fun delete(@RequestParam ids: List<Long>): Mono<Result<String>> {
        // 删除菜品
        return dishRepository.findAllById(ids)
            .collectList()
            .flatMap { dishes ->
                val dishIds = dishes.map { it.id!! }
                // Check if any dish is on sale
                val dishOnSale = dishes.any { it.status == ENABLE }
                if (dishOnSale) {
                    Mono.error(DeletionNotAllowedException(DISH_ON_SALE))
                } else {
                    setmealDishRepository.countAllByDishIdIn(dishIds)
                        .flatMap { count ->
                            if (count==0) {
                                dishRepository.deleteAll(dishes)
                                    .then(dishFlavorRepository.deleteAllByDishIdIn(dishIds))
                                    .then(Mono.fromCallable { Result.success(ACTION_SUCCESS) })
                            } else {
                                Mono.error(DeletionNotAllowedException(DISH_BE_RELATED_BY_SETMEAL))
                            }
                        }
                }
            }
            .publishOn(Schedulers.boundedElastic())
            .doOnSuccess {
                val key = "dish_*"
                cleanCache(key).subscribe()
            }
    }

    /**
     * 根据id查询菜品
     */
    @Operation(summary = "根据id查询菜品")
    @GetMapping("/{id}")
    fun getById(@PathVariable id: Long): Mono<Result<DishVO>> {

        // 首先，获取Dish
        return dishRepository.findById(id).flatMap { dish ->
            // 然后，根据Dish的ID获取Flavors
            dishFlavorRepository.findDishFlavorByDishId(id).collectList().map { flavors ->
                val dishVO = DishVO()
                BeanUtils.copyProperties(dish, dishVO)
                dishVO.flavors = flavors
                Result.success(dishVO)
            }
        }

        //return dishRepository.findById(id).map {
        //    val dish = DishVO()
        //    BeanUtils.copyProperties(it, dish)
        //    dish
        //}.flatMap {
        //    val flavors = dishFlavorRepository.findDishFlavorByDishId(id)
        //    it.flavors = flavors.collectList().block()
        //    Mono.just(Result.success(it))
        //}

    }

    /**
     * 修改菜品
     */
    @Operation(summary = "修改菜品")
    @Transactional
    @PutMapping
    fun update(@RequestBody @Validated dishDTO: DishDTO,exchange:ServerWebExchange): Mono<Result<String>> {
        val userId = (exchange.attributes[USER_ID] as Int).toLong()
        val dish = Dish()
        BeanUtils.copyProperties(dishDTO, dish)
        dish.updateTime = Instant.now()
        dish.updateUser = userId

        return dishService.updateDish(dish)
            .filter { it > 0 }
            .flatMap {
                dishFlavorRepository.deleteDishFlavorByDishId(dish.id!!)
            }.flatMap {
                val flavors = dishDTO.flavors?.map {
                    it.dishId = dishDTO.id!!
                    it
                }
                log.info("flavors {}",flavors)
                if (!flavors.isNullOrEmpty()) {
                    dishFlavorRepository.saveAll(flavors).then(Mono.just(Result.success(ACTION_SUCCESS)))
                } else {
                    Mono.just(Result.success(ACTION_SUCCESS))
                }
            }.onErrorResume {
                log.info("错误信息 {}",it.message)
                Mono.just(Result.error(ACTION_FAIL))
            }
            .map {
                Result.success(ACTION_SUCCESS)
            }.switchIfEmpty(Mono.just(Result.error(ACTION_FAIL)))
            .publishOn(Schedulers.boundedElastic())
            .doOnSuccess {
                val key = "dish_${dishDTO.categoryId}"
                cleanCache(key).subscribe()
            }
    }

    /**
     * 根据分类查询菜品
     */
    @Operation(summary = "根据分类查询菜品")
    @RequestMapping("/list")
    fun list(@RequestParam @NotNull categoryId: Long): Mono<Result<List<Dish>>> {
        return dishRepository.findDishByCategoryId(categoryId).collectList().map {
            Result.success(it)
        }
    }

    /**
     * 清除缓存
     */
    fun cleanCache(pattern: String): Mono<Void> {
        val scanOptions = ScanOptions.scanOptions().match(pattern).build()
        return reactiveRedisTemplate.scan(scanOptions) // 正确使用 scan 方法
            .flatMap { key ->
                reactiveRedisTemplate.delete(key) // 删除找到的每个键
            }
            .then() // 在所有删除操作完成后返回一个 Mono<Void>

    }
}